作品 Demo 連結: 傳送門
作品目標:透過 input 調整 CSS 變數、重新渲染畫面
難易度:★★☆☆☆
HTML
<h2>Update CSS Variables with <span class='hl'>JS</span></h2>
<div class="controls">
<label for="spacing">Spacing:</label>
<input id="spacing" type="range" name="spacing" min="10" max="200" value="10" data-sizing="px">
<label for="blur">Blur:</label>
<input id="blur" type="range" name="blur" min="0" max="25" value="10" data-sizing="px">
<label for="base">Base Color</label>
<input id="base" type="color" name="base" value="#ffc600">
</div>
<img src="https://source.unsplash.com/7bwQXzbF6KE/800x500">
CSS
:root {
--base: #ffc600;
--spacing: 10px;
--blur: 10px;
}
img {
padding: var(--spacing);
background-color: var(--base);
filter: blur(var(--blur));
}
.hl {
color: var(--base);
}
平常是使用 SCSS 的我第一次聽到 CSS 早已有內建變數還蠻 Shock 的!
CSS 所有的變數都必須儲存在 :root 的 Scope 內
宣告的方法是在名稱加上前綴 double dash --
而引用的方法就是在要放入變數的地方寫入 var(variableName)
非常的簡單!
(雖然我還是會繼續用更方便的 SCSS 就是了... 呵呵... ^^"
接下來我們就進入正式的 原生JavaScript 吧!
const inputs = document.querySelectorAll('input');
在這裡我們使用選取器先取得所有的 input 元素。
因為作者都是使用 ES6+ 的語法,所以決定從今天開始也跟進
畢竟也是未來的趨勢!
ES6+ 的變數宣告有三種
1. const
2. let
3. var
const - 宣告出來的變數是 不可變動 的
const 的作用範圍為 全域(但不會是 window 的屬性) 或是 區塊
你無法給 const 重新指派,否則 console 會報錯
但如果內容物為 物件 的話,改變物件內的 key/value 是合法的呦!
let - 內容物是可以變更的
let 的作用範圍為 所在的區塊,以及該區塊包含的子區塊
var 則是跟 ES5 是相同的東西
var 的作用範圍為 全域(window 的屬性) 或是 整個 function
但是如果都用 ES6+ 的寫法,則是強烈建議不要使用此方法
關於 ES6 的宣告可以參考這篇文章的詳細介紹:傳送門
inputs.forEach(input => input.addEventListener('change', inputHandler));
inputs.forEach(input => input.addEventListener('mousemove', inputHandler));
在此我們第一個綁定的是 change 事件
也就是在 input 數值確認改變之後,即執行後方的函數
那麼為什麼我們還需要再綁定一個 mousemove 事件呢?
那是因為 change 只有在"確認"要改變的值才會觸發
在我們這個地方的意思就是 當你拖拉 range input 時候並不會有任何反應
只有在你滑鼠左鍵放掉時才會觸發
但我們希望拖拉時就可以動態改變,
因此才再次加上了 mousemove 的事件
在此我們也再次使用到了 ES6+ 的新東西: 箭頭函數
如果轉譯為 ES5 的語法的話會如下
inputs.forEach(function(input){
input.addEventListener('change', inputHandler)
});
箭頭函數前方的 input 為參數,
在 forEach 當中代表的是當下的 Element
參數的寫法會有三種狀況:
1. 沒有參數時: () => {}
2. 一個參數時可省略小括弧: parameterName => {}
3. 兩個(含)以上的參數時: (a, b) => {}
相關詳細說明請參考此處的文章: 傳送門
// HTML 內的 input 參考
// <input id="spacing" type="range" name="spacing" min="10" max="200" value="10" data-sizing="px">
// <input id="blur" type="range" name="blur" min="0" max="25" value="10" data-sizing="px">
// <input id="base" type="color" name="base" value="#ffc600">
function inputHandler(){
let suffix = this.dataset.sizing || '';
document.documentElement.style.setProperty('--' + this.name, this.value + suffix);
}
當我們 console 出 input 的數值
會發現只有純數值、沒有單位,導致無法使用
所以我們就要使用之前學過的自定義屬性 data-* 來自動加上單位
因為我們並不是每個 input 都有 data-sizing 屬性,
所以需要使用 this.dataset.sizing || '' 來為當前的 input 取值並指定給 suffix 變數,
避免 suffix 成為 undefined 狀態造成程式錯誤。
最後我們來把從 input 取得的數值傳到我們的根元素 <html> 上
document.documentElement == <html>
然後在其上使用 .style.setProperty 設置一個 style 的屬性
'--' + this.name, this.value + suffix
'--' 為 CSS 變數的前綴詞
this.name 為取得當前 input 的 name 屬性(可參考 HTML)
this.value 為取得當前 input 更動後的值
最後再加上後綴詞 suffix 變數(可能是 px 或是 空字串)
就完成囉!!
透過開發者工具可以看見 <html> 內的 style 會隨著你 input 的改變而跟著變動,
改變 CSS 變數後進而重新渲染整個畫面,大功告成!!
LEVEL UP!! (灑花